#include "../include/thread_pool.h"

int removeTrailingSpace(char* str) {
    int length = strlen(str);
    if (length > 0 && str[length - 1] == ' ') {
        str[length - 1] = '\0';
    }   
    return 0;
}

FileEntry* selectFileEntryByFileNameParentIdOwnerId(const char* fileName, int parentId, int ownerId) {
    MYSQL *conn = create_db_connection();
    char query[256];
    snprintf(query, sizeof(query), "SELECT * FROM fileentry WHERE filename = '%s' AND parent_id = %d AND owner_id = %d", fileName, parentId, ownerId);

    if (mysql_query(conn, query)) {
        fprintf(stderr, "selectFileEntryByFileNameParentIdOwnerId() failed: %s\n", mysql_error(conn));
        mysql_close(conn);
        return NULL;
    }

    MYSQL_RES *res = mysql_store_result(conn);
    if (res == NULL) {
        fprintf(stderr, "mysql_store_result() failed: %s\n", mysql_error(conn));
        mysql_close(conn);
        return NULL;
    }

    MYSQL_ROW row;
    FileEntry *fileEntry = NULL;
    if ((row = mysql_fetch_row(res))) {
        fileEntry = (FileEntry *)malloc(sizeof(FileEntry));
        fileEntry->id = atoi(row[0]);
        fileEntry->parentId = atoi(row[1]);
        strcpy(fileEntry->fileName, row[2]);
        fileEntry->ownerId = atoi(row[3]);
        strcpy(fileEntry->md5, row[4]);
        fileEntry->fileSize = atoi(row[5]);
        fileEntry->fileType = atoi(row[6]);
    }

    mysql_free_result(res);
    mysql_close(conn);
    return fileEntry;
}

int removeLastPathComponent(const char* path, char** parentPath) {
    if (path == NULL || strlen(path) == 0)
        return -1;

    char* copy = strdup(path); // 复制输入路径以避免修改原始字符串
    char* lastSlash = strrchr(copy, '/'); // 查找最后一个斜杠字符

    if (lastSlash == NULL) {
        free(copy);
        return -1;
    }

    *lastSlash = '\0'; // 将最后一个斜杠替换为字符串结束符

    *parentPath = strdup(copy); // 复制前面的内容
    free(copy); // 释放复制的字符串内存

    return 0;
}

int cdCommand(task_t* task) {

    char buff[512] = {0};
    char* filename = NULL;
    char path[128] = {0};

    // 根据username查表获取user表信息，再通过user表信息获取pwd
    char * username = task->user->userName;
    User * user = selectUserByUserName(username);
    char * pwd = user->pwd;
    // printf("初始进入cd命令时，此时的pwd为%s\n", pwd);

    // 移除指令后的空格
    removeTrailingSpace(task->data);

    // 1. 判断cd 参数是否合法
    if (strcmp(task->data, "") == 0) {
        strcpy(buff, "请输入正确的cd 命令!\n");
        strcat(buff, "\0");
        sendn(task->peerfd, buff, strlen(buff));
        return -1;

    } else if (strcmp(task->data, ".") == 0 || strcmp(task->data, "./") == 0) {
        sprintf(buff, "当前路径为>%s", pwd);
        sendn(task->peerfd, buff, strlen(buff));
        return 0;

    } else if (strcmp(task->data, "../") == 0 || strcmp(task->data, "..") == 0) {
        printf("进入../\n");
        //  printf("1.此时pwd的值为：%s\n",pwd);
        // 1. 先获取当前目录的虚拟文件表信息
        char tmp[64] = {0};
        strcpy(tmp, pwd); // 用tmp暂存当前的pwd,防止被更改
        char *tmpdir = NULL;
        getCurrentDirectory(tmp, &tmpdir);
        FileEntry * curr =  selectFileEntryByFileName(tmpdir);
        // 1.1 判断当前的目录虚拟文件表的parent_id是否是0
        if (curr->parentId == 0) {
            // 当前已经是根目录，返回提示
            sprintf(buff, "当前已在根目录!\n");
            sendn(task->peerfd, buff, strlen(buff));
            return 0;
        }
        // 1.2 当前目录虚拟文件表的parent_id不是0，代表还有上层目录
        // 获取要切换的目录
        // printf("获取父目录前的pwd为:%s\n", pwd);
        char * res = NULL;
        removeLastPathComponent(pwd, &res);
        // printf("获取父目录的结果为:%s\n", res);
        strcpy(path, res);
        printf("当前的目录为%s\n", path);
        sprintf(buff, "当前路径为>%s\n", path);
        // 发送给客户端
        sendn(task->peerfd, buff, strlen(buff));
        // 更新user表的pwd信息
        strcpy(user->pwd, path);
        updateUser(user);
        return 0;

    } else {

        // cd 到 mc,mc一定在当前目录下，需要查找当前目录下是否存在对应的文件名
        // 获取当前目录的虚拟文件表
        char temp[64] = {0};
        strcpy(temp,pwd);
        char *tmpdir = NULL;
        getCurrentDirectory(temp, &tmpdir);
        FileEntry * curr = selectFileEntryByFileName(tmpdir);

        // 获取要切换的目录名
        getCurrentDirectory(task->data, &filename);
        // 判断切换的目录是否存在
        FileEntry* dir = selectFileEntryByFileNameParentIdOwnerId(filename, curr->id, curr->ownerId);

        if (dir == NULL) {
            sprintf(buff, "要切换的目录不存在!\n");
            sendn(task->peerfd, buff, strlen(buff));
            return -1;
        }

        bzero(path,sizeof(path));
        strcat(path, pwd); // 添加当前pwd
        strcat(path, "/");
        strcat(path, task->data);
        sprintf(buff, "当前路径为>%s", path);
        sendn(task->peerfd, buff, strlen(buff));

        // 更新当前path
        strcpy(user->pwd, path);
        updateUser(user); // 将更改发送到数据库

        return 0;
    }
}

int getCurrentDirectory(const char* pwd, char** CurrentDirectory) {
    const char* delimiter = "/";
    char* token = NULL;
    char* lastToken = NULL;

    // 使用 strtok 函数逐个切割路径
    token = strtok((char*)pwd, delimiter);
    while (token != NULL) {
        lastToken = token;
        token = strtok(NULL, delimiter);
    }

    if (lastToken != NULL) {
        // 存储目录名
        *CurrentDirectory = strdup(lastToken);
        return 0;
    }

    return 0;
}

int getParentDirectory(const char* pwd, char** ParentDirectory) {
    const char* delimiter = "/";
    char* token = NULL;
    char* lastToken = NULL;
    char* secondLastToken = NULL;

    // 使用 strtok 函数逐个切割路径                                                                                                         
    token = strtok((char*)pwd, delimiter);
    while (token != NULL) {
        secondLastToken = lastToken;
        lastToken = token;
        token = strtok(NULL, delimiter);
    }

    if (secondLastToken != NULL) {
        // 创建一个新的字符串存储上级目录
        *ParentDirectory = strdup(secondLastToken);
        return 0;
    }

    return -1;
}

int lsCommand(task_t * task)
{
    int parent_id; // 保存用户当前父目录id
    char buff[1024] = {0};
    char* filename = NULL;

    // 根据当前的username 找到对应的use表信息从而得到对应的pwd,保存到path里
    char * username = task->user->userName;
    User * user = selectUserByUserName(username);
    char * path = user->pwd;

    // 1. 判断ls后是否有参数
    if (strcmp(task->data, "") == 0) {
        // 当前ls后面无参数
        getCurrentDirectory(path, &filename);
    } else if (strcmp(task->data, "./") == 0 || strcmp(task->data, ".") == 0){
        getCurrentDirectory(path, &filename);
    } else if (strcmp(task->data, "../") == 0 || strcmp(task->data, "..") == 0) {
        getParentDirectory(path, &filename);
        if (filename == NULL) {
            strcpy(buff, "当前已在根目录，重新输入。\n");
            strcat(buff, "\0");
            sendn(task->peerfd, buff, strlen(buff));
            return -1;
        }
    } else {
        getCurrentDirectory(task->data, &filename);
    }

    // 2. 获取filename 对应的id
    FileEntry* dir = selectFileEntryByFileName(filename);
    if (dir == NULL) {
        strcpy(buff, "数据库找不到对应的目录，重新输入。\n");
        strcat(buff, "\0");
        sendn(task->peerfd, buff, strlen(buff));
        return -1;
    }

    parent_id = dir->id; // 保存对应id

    free(dir);

    int num = 0;
    // 3. 根据parent_id 查询对应的记录
    FileEntry* reslist = selectFileEntryByparentId(parent_id, &num);

    // 4. 写入ls数据
    if (reslist == NULL) {
        sprintf(buff, " ");
    }
    else {
        for (int i = 1; i <= num; i++) {
            if (reslist[i-1].fileType == 0) { // 目录
                sprintf(filename, "\033[1;92m%-8s\033[0m\t", reslist[i-1].fileName); // 蓝色
            } else if (reslist[i-1].fileType == 1) { // 文件
                sprintf(filename, "\033[0;93m%-8s\033[0m\t", reslist[i-1].fileName); // 绿色
            }
            strcat(buff, filename);
            if (i % 5 == 0) {
                strcat(buff, "\n");
            }
        }
        strcat(buff, "\0");
    }
    sendn(task->peerfd, buff, strlen(buff));

    return 0;
}

int mkdirCommand(task_t * task) {

    char * dirname; // 用于暂存要插入的字符
    char buff[512] = {0}; // 保存给用户发送的内容

    // 1. 先通过当前的username获取用户表信息，从而获取pwd
    char * username = task->user->userName;
    User * user = selectUserByUserName(username);
    char * pwd = user->pwd;


    // 移除指令后的空格
    removeTrailingSpace(task->data);

    // 2. 获取当前目录的虚拟文件表
    int num = 0;
    char *tmpdir = NULL;
    getCurrentDirectory(pwd, &tmpdir);
    FileEntry * curr = selectFileEntryByFileNameAndOwnerId(tmpdir, user->id, &num);

    // 3. 查询当前目录下是否已存在同名目录
    dirname = task->data;
    FileEntry * dir = selectFileEntryByFileNameParentIdOwnerId(dirname, curr->id, user->id);
    if (dir) {
        // dir存在，说明已存在同名目录，返回
        sprintf(buff, "%s已存在", dirname);
        strcat(buff, "\0");
        sendn(task->peerfd, buff, sizeof(buff));
        return -1;
    }
    // dir不存在，构建dir的信息

    FileEntry * newdir = (FileEntry*)calloc(1, sizeof(FileEntry));
    newdir->parentId = curr->id;
    strcpy(newdir->fileName, dirname);
    newdir->ownerId = user->id;
    strcpy(newdir->md5, "0");
    newdir->fileSize = 0;
    newdir->fileType = 0;

    int ret = addFileEntry(newdir);
    if (ret == -1) {
        sprintf(buff, "创建失败！");
        sendn(task->peerfd, buff, sizeof(buff));
        return -1;
    }

    sprintf(buff, "创建成功！");
    sendn(task->peerfd, buff, sizeof(buff));
    return 0;
}

int removeCommand(task_t * task){
    //接收任务中的文件名
    removeTrailingSpace(task->data);
    if(strlen(task->data)==0){
        const char *msg="删谁我请问呢";
        sendn(task->peerfd,msg,strlen(msg));
        return -1;
    }

    char* pwd = NULL;
    getUserPWD(task->user->userName, &pwd);
    printf("----------%s\n",pwd);
    //拼接用户当前工作目录和用户输入文件，用以检查是否存在
    char fullPath[2000];
    snprintf(fullPath,sizeof(fullPath),"%s/%s",pwd,task->data);
    printf("--------fullPath:%s\n",fullPath); 
    //获取完整路径对应的条目
    FileEntry*entry=getEntryByPath(fullPath);
    if(entry==NULL){
        const char*msg="文件不存在";
        sendn(task->peerfd,msg,strlen(msg));
        return -1;
    }

    //检查条目类型，如果是文件返回错误信息
    if(entry->fileType==0){
        const char *msg="删目录不用这个";
        sendn(task->peerfd,msg,strlen(msg));
        return -1;
    }
    //文件直接删除
    deleteFileEntry(entry->id);

    //删除成功
    const char *msg="文件删完了";
    sendn(task->peerfd,msg,strlen(msg));

    return 0;
}


//递归删除目录及文件
int recRemoveDir(int dirId){
    //获取指定目录ID下的所有文件和子目录
    FileEntry *entries =getEntriesInDir(dirId);
    if(entries==NULL){
        printf("目录为空，删除目录：%d\n", dirId);
        deleteFileEntry(dirId);
        return 0;
    }
    //遍历目录下所有条目
    for(int i=0;entries[i].id!=0;i++){
        if(entries[i].fileType==1){
            //如果是文件直接删除
            deleteFileEntry(entries[i].id);
        }else{
            //如果是子目录则递归删除
            recRemoveDir(entries[i].id);
        }
    }

    //删除当前目录
    deleteFileEntry(dirId);
    free(entries);
    return 0; 
}

int rmdirCommand(task_t *task){
    //接收任务中的目录
    removeTrailingSpace(task->data);
    if(strlen(task->data)==0){
        const char *msg="删哪个";
        sendn(task->peerfd,msg,strlen(msg));
        return -1;
    }

    char* pwd = NULL;
    getUserPWD(task->user->userName, &pwd);
    //拼接用户当前工作目录和用户输入目录，用以检查是否存在
    char fullPath[2000];
    snprintf(fullPath,sizeof(fullPath),"%s/%s",pwd,task->data);

    //获取完整路径对应的条目
    FileEntry*entry=getEntryByPath(fullPath);
    if(entry==NULL){
        const char*msg="不存在的目录";
        sendn(task->peerfd,msg,strlen(msg));
        return -1;
    }

    //检查条目类型，如果是文件返回错误信息
    if(entry->fileType==1){
        const char *msg="我删不了文件";
        sendn(task->peerfd,msg,strlen(msg));
        return -1;
    }
    printf("id=%d\n",entry->id);
    //递归删除目录及内容
    recRemoveDir(entry->id);

    //删除成功
    const char *msg="目录删掉了,里面的文件也没了";
    sendn(task->peerfd,msg,strlen(msg));
    return 0;
}

// 服务端
int putsCommand(task_t * task) {
    
    char response[64] = {0};

    // 1. 服务端接收客户端的md5
    char md5String[33];
    recvn(task->peerfd, md5String, sizeof(md5String));
    printf("收到的md5值是:%s\n", md5String);
    removeTrailingSpace(task->data);

    // 2. 查表判断是否已存在md5值相同的文件，存在发送1，不存在返回0
    FileInfo * file = selectFileInfo(md5String, 32);
    int ret = -1; // 标记文件是否存在
    if (file) {
        // 2.1.1 文件存在，给客户端发送ret = 1
        ret = 1;
        sendn(task->peerfd, &ret, sizeof(int));
        
        // 2.1.2 在虚拟文件表插入文件信息
        // 找到当前目录所在位置文件表
        // 根据username获取当前user表信息，通过user表信息获取pwd
        char * username = task->user->userName;
        User * user = selectUserByUserName(username);
        char * pwd = user->pwd;
        
        // 根据pwd,查找到当前所在的目录的虚拟文件表信息
        char tmp[64] = {0};
        strcpy(tmp, pwd);
        int num = 0;
        
        char *tmpdir = NULL;
        getCurrentDirectory(tmp, &tmpdir);
        FileEntry * curr = selectFileEntryByFileNameAndOwnerId(tmpdir, user->id, &num);
        
        // 填充新文件的FileEntry信息
        FileEntry * newfile = (FileEntry*)calloc(1, sizeof(FileEntry));
        newfile->parentId = curr->id;
        strcpy(newfile->fileName, task->data);
        newfile->ownerId = user->id;
        strcpy(newfile->md5, md5String);
        newfile->fileSize = 0;
        newfile->fileType = 1;
            

        // 向客户端发送返回结果
        int ret = addFileEntry(newfile);
        printf("执行到了这里!\n");
        if (ret == -1) {
            sprintf(response, "上传文件失败!");
            sendn(task->peerfd, response, sizeof(response));
            return -1;
        }
        
        printf("2执行到了这里!\n");

        sprintf(response, "秒传，上传文件成功!");
        int reslength = strlen(response);
        sendn(task->peerfd, &reslength, sizeof(int));
        sendn(task->peerfd, response, reslength);
        
        printf("3执行到了这里!\n");
        return 0;
    }

    // 3. 文件不存在
    // 发送ret结果
    ret = 0;
    sendn(task->peerfd, &ret, sizeof(ret));
    // 3.1 获取客户端发送的文件大小
    long int filelength = 0;
    recvn(task->peerfd, &filelength, sizeof(filelength));
    printf("接受到文件大小为%ld\n", filelength);

    // 3.2 新建文件，写入数据
    int fd = open(task->data, O_WRONLY | O_CREAT, 0664);
    if (fd < 0) {
        fprintf(stderr, "文件打开失败\n");
    }

    ret = 0;
    char buff[1000];
    off_t left = filelength;
    while (left > 0) {
        if (left < 1000) {
            ret = recvn(task->peerfd, buff, left); 
        } else {
            ret = recvn(task->peerfd, buff, sizeof(buff));
        }
        if (ret < 0) {
            break;
        }
        ret = write(fd, buff, ret);
        left -= ret;
    }
    close(fd);

    // 3.3 将信息写入数据库
    // 3.3.1 将信息写入文件表
    FileInfo * newinfo = (FileInfo*)calloc(1, sizeof(FileInfo));
    strcpy(newinfo->md5, md5String);
    strcpy(newinfo->fileName, task->data);
    addFileInfo(newinfo);

    // 3.3.2 将文件信息写入虚拟文件表
    // 找到当前目录所在位置文件表
    // 根据username获取当前user表信息，通过user表信息获取pwd
    char * username = task->user->userName;
    User * user = selectUserByUserName(username);
    char * pwd = user->pwd;
        
    // 根据pwd,查找到当前所在的目录的虚拟文件表信息
    char tmp[64] = {0};
    strcpy(tmp, pwd);
    int num = 0;
    char *tmpdir = NULL;
    getCurrentDirectory(tmp, &tmpdir);
    FileEntry * curr = selectFileEntryByFileNameAndOwnerId(tmpdir, user->id, &num);
        
    // 填充新文件的FileEntry信息
    FileEntry * newfile = (FileEntry*)calloc(1, sizeof(FileEntry));
    newfile->parentId = curr->id;
    strcpy(newfile->fileName, task->data);
    newfile->ownerId = user->id;
    strcpy(newfile->md5, md5String);
    newfile->fileSize = filelength;
    newfile->fileType = 1;
     
    // 向客户端发送返回结果
    ret = addFileEntry(newfile);
    if (ret == -1) {
        sprintf(response, "上传文件失败!");
        sendn(task->peerfd, response, sizeof(response));
        return -1;
    }
        
    sprintf(response, "写入文件,上传文件成功!");
    sendn(task->peerfd, response, sizeof(response));
    return 0;
}

int getsCommand(task_t* task) {
    
    // 服务端
    // 1. 查询文件是否存在，向客户端发送信息是否存在的响应
    // 2. 接收客户端发送的offset
    // 3. 向客户端发送文件
    removeTrailingSpace(task->data);
    
    char * filename = task->data;
    
    // 1. 查询文件是否存在
    
    // 1.1 根据用户名找到用户对应的user表的信息
    char * username = task->user->userName;
    User * user = selectUserByUserName(username);
    // char * pwd = user->pwd;

    // 1.2 根据用户id和文件名查找文件是否存在
    int num = 1;
    int flag = 0; // 标志位，标记文件是否存在 0不存在，1存在
    FileEntry* file = selectFileEntryByFileNameAndOwnerId(filename, user->id, &num);
    if (file == NULL) {
        // 文件不存在，向客户端发送flag = 0
        flag = 0;
        sendn(task->peerfd, &flag, sizeof(flag));
        return -1;
    } else {
        // 文件存在，向客户端发送flag = 1
        flag = 1;
        sendn(task->peerfd, &flag, sizeof(flag));
    }

    // 1.3 因为使用了文件秒传，使用MD5标记文件的信息，
    // 需要根据虚拟文件表MD5的信息查询真正的文件名。
    FileInfo * real_file = selectFileInfo(file->md5, 32);
    filename = real_file->fileName;

    // 2. 获取文件长度
    int fd = open(filename, O_RDWR);
    struct stat st;
    bzero(&st, sizeof(st));
    fstat(fd, &st);
    
    int fileLength = st.st_size;
    
    // 2.1 发送文件长度
    sendn(task->peerfd, &fileLength, sizeof(fileLength));

    
    // 3. 向服务器发送文件内容
    char buff[1000];
    int curr = 0;
    int ret = 0;
    while(curr < fileLength) {
        bzero(buff, sizeof(buff));
        ret = read(fd, buff, sizeof(buff));
        if (ret == 0) {
            break;
        }
        ret = sendn(task->peerfd, buff, ret);
        curr += ret;
    }
    close(fd);
    printf("发送完毕!\n");
    return 0;
} 

int pwdCommand(task_t * task) {

    // 1. 获取当前username
    char * username = task->user->userName;

    // 2. 根据当前username查到对应的user_info得到对应的pwd
    User * user = selectUserByUserName(username);

    // 3. 获取当前的pwd
    char * path = user->pwd;

    char buff[64];
    strcpy(buff, path);
    
    // 4. 发送给客户端信息
    sendn(task->peerfd, buff, sizeof(buff));
    return 0;

}
